import formula from '@/components/custom-calculation/formula.js';
import store from '@/store.js';
import storage from '@/utils/ado-self/ado-storage';
import { IMGURL_DISPLAY } from '@/utils/constant';
import { $demoi18n } from '@/utils/lang';
import whiteList from '@/utils/xss/tag-white-list.js';
import { ElMessage as Message, ElMessageBox } from 'element-plus';
import funs from 'lib/local/funs.js';
import wecal from 'lib/weCal/weCal.js';
import _ from 'lodash';
import { defineAsyncComponent, markRaw } from 'vue';
import xss from 'xss';
import { getPageId as _getPageId } from './util-common';
import colorBoardAll from '@/common/color_board_all.js';

const vue = {};

export const _xss = (x, options = {}) => {
  const opWhiteList = options?.whiteList || {};
  const _options = {
    ...options,
    whiteList: {
      ...whiteList,
      ...opWhiteList,
    },
    css: false,
  };

  if (x === 0) {
    return x;
  }
  return xss(x, _options);
};

export const objHas = (obj, key) => {
  if (!obj) {
    return false;
  }
  return Object.prototype.hasOwnProperty.call(obj, key);
};

// 防抖
export const debounce = function(fn, delay) {
  let timer = null;
  return function(...args) {
    const context = this;
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(context, args);
    }, delay);
  };
};

// 格式化指标图表的x轴字段
export const strSplit = function(s) {
  let s3 = '';
  if (!s || s.trim() === '') {
    return '-';
  }
  const _s = s.trim();
  const a1 = _s.split('_');
  const s2 = a1[a1.length - 1];
  const a2 = s2.split('-');
  s3 = a2[a2.length - 1];
  s3 = s3.substring(0, 3);
  return s3;
};

export const getServerImgUrl = (imgId, type = 'xvue', isPreview = false) => {
  if (!imgId) {
    return '';
  }
  const baseUrl = window.isLocalEnv ? window.visualBaseUrl : window._adoPromiseBaseUrl;
  let imgUrl = '';
  if (type === 'platform') {
    // 平台图片
    imgUrl = `${baseUrl}/quality/api/v1/file/download/${imgId}`;
  } else {
    imgUrl = `${baseUrl}/xvue-admin/api/files/${imgId}`;

    // 本地没有真实组件，而真实组件保存后才会绑定组件跟图片关系（图片才可见），所以本地需要preview预览显示
    if (isPreview || window.isLocalEnv) {
      imgUrl += '?preview=1';
    }
  }

  if (process.env.NODE_ENV === 'development') {
    // 本地环境直接用其他图片代替
    imgUrl = `/sdv/localApi/image/${imgId}`;
  }

  return imgUrl;
};

export const getServerImgUrlByIcon = (iconObj, isPreview = false) => {
  const { icon_id: iconId, apiType, serType } = iconObj || {};
  let imgUrl = getServerImgUrl(iconId, apiType, isPreview);
  if (serType === 5 || serType === 9) {
    const splitor = imgUrl.includes('?') ? '&' : '?';
    imgUrl += `${splitor}type=${serType}`;
  }
  return imgUrl;
};

// 将正常图片链接转换成blob图片源码
export const convertImgUrlToBlob = imgUrl => {
  let url = imgUrl.replace(window._adoPromiseBaseUrl, '');
  url = url.replace(/^http(s?):\/\/[^/]*\//i, '/');
  return new Promise(resolve => {
    window
      ._UIDesingerAxios({
        url: url,
        type: 'post',
        responseType: 'blob',
        emulateJSON: true,
      })
      .then(res => {
        const blog = res.data.size === 0 ? false : res.data;
        resolve(blog);
      })
      .catch(err => {
        resolve(false);
      });
  });
};

export const getImgDataUrl = async(httpUrl, type = 'base64') => {
  const blob = await convertImgUrlToBlob(httpUrl);
  if (!blob) {
    return '';
  }
  const blobUrl = URL.createObjectURL(blob);
  return blobUrl;
};

export const getImgUrl = (requestAddress, imgId) => {
  let imgUrl;
  if (typeof imgId === 'object') {
    imgUrl = getServerImgUrlByIcon(imgId, false);
  } else {
    imgUrl = imgId ? `${requestAddress}${IMGURL_DISPLAY}${imgId}` : '';
  }
  return imgUrl;
};

/**
 * 每n个英文单词换一行
 * @param {*} str
 */
export const labelLeapOut = (str, n) => {
  if (!str) {
    return null;
  }
  let ret = '';
  const retArr = [];
  const reg = /[ {2}_]/g;
  const regstr = str.replace(reg, '$&\n');
  const arr = regstr.split('\n');
  if (arr.length <= 3) {
    ret = str;
  } else {
    for (let i = 0; i < arr.length; i++) {
      retArr.push(arr[i]);
      i++;
      arr[i] && retArr.push(arr[i]);
      i++;
      arr[i] && retArr.push(`${arr[i]}\n`);
    }
    ret = retArr.join(' ');
  }
  return ret;
};

export const timeFormat = time => {
  const y = time.getFullYear();
  const m = time.getMonth() + 1;
  const d = time.getDate();
  const hh = time.getHours();
  const mm = time.getMinutes();
  const ss = time.getSeconds();
  return (
    `${y < 10 ? `0${y}` : y
    }-${
      m < 10 ? `0${m}` : m
    }-${
      d < 10 ? `0${d}` : d
    } ${
      hh < 10 ? `0${hh}` : hh
    }:${
      mm < 10 ? `0${mm}` : mm
    }:${
      ss < 10 ? `0${ss}` : ss}`
  );
};

// 判断数值是否有效
export const isNum = value => {
  if (typeof value === 'undefined' || typeof value === 'object' || (typeof value === 'string' && !value.length)) {
    return false;
  }
  const v = Number(value);
  return !Number.isNaN(v);
};

// 数据格式调整 + 千分位分隔符
export const numFormat = function(data, state) {
  if (!isNum(data)) {
    return data;
  }

  // 添加千分位分隔符
  if (state) {
    const re = /\d{1,3}(?=(\d{3})+$)/g;
    const result = data.toString().replace(/^(\d+)((\.\d+)?)$/, (s, s1, s2) => s1.replace(re, '$&,') + s2);
    return result;
  } else {
    return data;
  }
};

// 小数位计算
export const getDecimal = (sum, decimal) => {
  if (!isNum(sum)) {
    return sum;
  }
  let _sum = Math.round(sum) / Math.pow(10, decimal);

  // 模拟toFixed自动补0功能
  _sum = String(_sum);
  if (_sum.indexOf('.') < 0) {
    if (decimal > 0) {
      _sum = `${_sum}.${'0'.repeat(decimal)}`;
    }
  } else {
    const arr = _sum.split('.');
    if (arr[1].length < decimal) {
      arr[1] = `${arr[1]}${'0'.repeat(decimal - arr[1].length)}`;
    }
    _sum = arr.join('.');
  }
  return _sum;
};

/*
 * 数值格式转千分位 + 小数点
 * 1700 => '1,700.00'
 */
export const numFormatToFixed = function(data, decimalCount = 2, noTailZero = false) {
  if (!isNum(data)) {
    return data;
  }
  const _data = Math.pow(10, decimalCount) * data;
  const dataArray = getDecimal(_data, decimalCount);
  let num = numFormat(dataArray, true);

  // 去除小数点尾部0
  if (noTailZero) {
    num = num.replace(/(\.[1-9]+)0*$/, '$1');
    num = num.replace(/\.0*$/, '');
  }

  return num;
};

/*
 * 对字符串里面的所有数字转千分符，
 * 如： 'hello 10000.23 world 9999' => 'hello 10,000.23 world 9,999'
 */
export const permil = (str, decimalCount) => {
  let _str = isNum(str) ? `${str}` : (str || '');
  const reg = /\d{1,3}(?=(\d{3})+$)/g;
  _str = _str.replace(/([^\d\.,]?)(\d+)((\.\d+)?)/g, (s, s1, s2, s3) => {
    if (s1 === '#') { // color: #123445
      return s;
    }
    let tail = s3;
    if (decimalCount) {
      tail = Number(`0${s3}`)
        .toFixed(decimalCount)
        .slice(1);
    }
    return s1 + s2.replace(reg, '$&,') + tail;
  });
  return _str;
};

// 字符串中的数字不做千分符分割替换
export const dePermil = str => {
  if (!str) {
    return str;
  }
  const _str = str.replace(/(\d+),(?=\d{3}([^\d]+|$))/g, '$1');
  return _str;
};

// 转换表单数据
export const convertFormData = function(tableData) {
  const form = {};
  for (const key in tableData) {
    if (tableData[key].mapping) {
      form[tableData[key].field] = tableData[key].mapping;
    }
  }
  return form;
};

export const getAggFnList = function() {
  const fnList = [];

  // 函数个数
  formula.forEach(item => {
    if (item.type === 'agg') {
      item.child.forEach(subItem => {
        fnList.push(subItem.name);
      });
    }
  });
  return fnList;
};

export const getXtagAggParams = (params, tableData) => {
  const aggFnList = getAggFnList(); // 聚合函数列表
  let isFirstAgg = true;
  let isFirstCustomFields = true;
  tableData.forEach(item => {
    // 是否包含聚合函数字段
    let hasFn = false;
    let fnName = '';
    aggFnList.forEach(name => {
      if (!hasFn && item.mapping.includes(`${name}(`)) {
        hasFn = true;
        fnName = name;
      }
    });

    // 判断是否自定义计算字段
    if (item.type || fnName) {
      // 自定义计算字段中有函数时的处理方式
      if (hasFn) {
        // 初始化 agg 字段
        if (isFirstAgg) {
          params.agg = {
            aggregateObjs: [],
          };
          isFirstAgg = false;
        }

        // 目前只能处理单函数 单参数 ,目前匹配第一个函数，第一个中括号的内容
        const reg = /\[(.+?)\]/g;
        const fields = item.mapping.match(reg);
        let field = '';
        if (fields.length) {
          field = fields[0];
        }

        params.agg.aggregateObjs.push({
          alias: item.mapping,
          field: field,
          aggregator: fnName,
        });
      } else {
        // 初始化 customFields 字段
        if (isFirstCustomFields) {
          isFirstCustomFields = false;
          params.customFields = [];
        }
        params.customFields.push({
          fieldsName: item.mapping,
          function: item.mapping,
        });
      }
    }
  });
};

// 获取自定义计算面板中 函数字段列表
export const getFnList = function() {
  const fnList = [];

  // 函数个数
  formula.forEach(item => {
    item.child.forEach(subItem => {
      fnList.push(subItem.name);
    });
  });
  return fnList;
};

export const getNormalFnList = function() {
  const fnList = [];

  // 函数个数
  formula.forEach(item => {
    if (item.type === 'normal') {
      item.child.forEach(subItem => {
        fnList.push(subItem.name);
      });
    }
  });
  return fnList;
};

export const convertTreeToList = (root, key) => {
  // 树转数组
  let stack = [];
  const array = [];
  const hashMap = {};
  stack = [...stack, ...root];
  const visitNode = (node, _hashMap, _array) => {
    if (!_hashMap[node[key]]) {
      _hashMap[node[key]] = true;
      _array.push(node);
    }
  };

  while (stack.length !== 0) {
    const node = stack.pop();
    if (node.children === null) {
      visitNode(node, hashMap, array);
    } else {
      for (let i = node.children.length - 1; i >= 0; i--) {
        stack.push(node.children[i]);
      }
      visitNode(node, hashMap, array);
    }
  }
  return array;
};

export const strlen = str => {
  let len = 0;
  for (let i = 0; i < str.length; i++) {
    const c = str.charCodeAt(i);

    // 单字节加1
    if ((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) {
      len++;
    } else {
      len += 2;
    }
  }
  return len;
};

// 判断是否空值
export const isEmpty = obj => {
  if (typeof obj === 'undefined' || obj === null || obj.toString() === '') {
    return true;
  } else {
    return false;
  }
};

// 判断是否undefined或null值
export const isUndefinedNull = obj => {
  if (typeof obj === 'undefined' || obj === null) {
    return true;
  } else {
    return false;
  }
};

// 获取页面pageId值
export const getPageId = vm => _getPageId(vm);

/**
 * 扁平数据转树结构
 * @param {array} data
 * @param {object} config
 * @example
 * -----------------------
 * formatTreeData(
 *   data,
 *   {
 *       parent: 'parent',
 *       name : 'name',
 *       children: 'children'
 *   }
 * );
 */
export const formatTreeData = (data, config = {}) => {
  const parent = config.parent || 'parent';
  const children = config.children || 'children';
  const name = config.name || 'name';
  const map = {};
  const jsonTree = [];
  data.forEach(v => {
    map[v[name]] = v;
  });
  data.forEach(v => {
    if (!v[parent]) {
      jsonTree.push(v);
      return true;
    }
    const pNode = map[v[parent]];
    if (pNode) {
      !pNode[children] && (pNode[children] = []);
      pNode[children].push(v);
    } else {
      jsonTree.push(v);
    }
    return true;
  });
  return jsonTree;
};

export const isObject = obj => Object.prototype.toString.call(obj) === '[object Object]';

export const isArray = arr => Object.prototype.toString.call(arr) === '[object Array]';

export const isString = arr => Object.prototype.toString.call(arr) === '[object String]';

// 合并对象，将后面的对象里面的属性合并到target对象
export const mergeObj = function(target, ...arg) {
  return arg.reduce((acc, cur) => Object.keys(cur).reduce((subAcc, key) => {
    const srcVal = cur[key];
    if (isObject(srcVal) && isObject(subAcc[key])) {
      subAcc[key] = mergeObj(subAcc[key] ? subAcc[key] : {}, srcVal);
    } else {
      subAcc[key] = srcVal;
    }
    return subAcc;
  }, acc), target);
};

// 获取url传参
export const getQueryValue = name => {
  const reg = new RegExp(`${name}=([^&]*)(&|$)`, 'i');
  const r = window.location.href.match(reg);
  if (r !== null) {
    return decodeURIComponent(r[1]);
  }
  return null;
};

/**
 * 简易获取对象属性方法
 * @param  {Object} res 对象，如： {a:{b:{c:123}}}
 * @param  {String} key 属性，如： 'a.b.c'
 * @return {mix}        返回false 代表属性值本身是false或者无目标属性
 * @example
 * ----------------------
 *   getObjAttrVal(res, 'a.b.c') => 123
 */
export const getObjAttrVal = (res, key) => {
  const keys = key.split('.'); // 'a.b.c'
  let _res = res;
  for (const i of keys) {
    if (!objHas(_res, i)) {
      return false;
    }
    _res = _res[i];
  }
  return _res;
};

// 获取当前系统语言
export const getLang = () => {
  let lang = getQueryValue('lang');
  if (!lang) {
    lang = (window._adoPremise && window._adoPremise.language) || 'zh-CN';
  }
  return lang;
};

// 删除数组中的某个元素
export const dropArrItem = (arr, item) => {
  const index = arr.indexOf(item);
  let _arr = arr;
  if (index > -1) {
    // 子元素是字符串，数值，或同一份内存，该方式不会改变arr内存地址
    _arr.splice(index, 1);
  } else {
    // 以下会全部删除符合条件项目，跟上面逻辑不一致，不完美，待优化
    if (_.isPlainObject(item)) {
      // 如果子元素是普通对象{}，匹配子项删除
      _arr = _.dropWhile(_arr, item);
    } else if (isArray(item)) {
      // 子项是数组 [1,2]
      _arr = _.differenceWith(_arr, [item], _.isEqual);
    } else {}
  }
  return _arr;
};

// html 反转义
export const htmlDecode = text => {
  let temp = document.createElement('div');
  temp.innerHTML = _xss(text);
  let html = temp.innerText || temp.textContent;
  html = _xss(html);
  temp = null;
  return html;
};

// 适配xvue 转码
export const transcode = (str, noScript = false) => {
  let _str = str;
  _str = _str.replace(/&amp;/g, '&');
  _str = _str.replace(/&lt;/g, '<');
  _str = _str.replace(/&gt;/g, '>');
  _str = _str.replace(/&quot;/g, '"');
  if (noScript) {
    _str = _str.replace(/<\/?script\s?[^>]*>/g, '').replace(/on[a-z]*\s*=\s*['"][^'"]*['"]/gi, '');
  }
  return _str;
};

// 转义后端返回的字段
export const escape = str => {
  let _str = str;
  _str = _str.replace(/&amp/g, '&');
  _str = _str.replace(/&lt/g, '<');
  _str = _str.replace(/&gt/g, '>');
  _str = _str.replace(/&#x27/g, '\'');
  _str = _str.replace(/&#40/g, '(');
  _str = _str.replace(/&#41/g, ')');
  _str = _str.replace(/&#x2F/g, '\\');
  _str = _str.replace(/&#x2F/g, '/');
  return _str;
};

// 执行公式运算，如 evalFormula('(1+2)/3') => 1
export const evalFormula = formulaStr => {
  let temp = document.createElement('div');
  temp.innerHTML = _xss(formulaStr);
  const _formula = temp.innerText.trim();
  temp = null;

  const value = wecal.dealBracket(_formula);
  if (isNaN(value)) {
    return '';
  }
  return value;
};

// 时间格式化，如 dateFormat('yyyy-MM-dd hh:mm:ss', newDate()) => '2020-09-07 17:24:26'
export const dateFormat = (fmt, date) => {
  const o = {
    'M+': date.getMonth() + 1, // 月份
    'd+': date.getDate(), // 日
    'h+': date.getHours(), // 小时
    'm+': date.getMinutes(), // 分
    's+': date.getSeconds(), // 秒
    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds(), // 毫秒
  };
  let _fmt = fmt;
  if (/(y+)/.test(_fmt)) {
    _fmt = _fmt.replace(RegExp.$1, (`${date.getFullYear()}`).substr(4 - RegExp.$1.length));
  }
  for (const k in o) {
    if (new RegExp(`(${k})`).test(_fmt)) {
      _fmt = _fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? o[k] : (`00${o[k]}`).substr((`${o[k]}`).length));
    }
  }
  return _fmt;
};

// 根据name 尝试获取自定义值颜色，没有则返回套餐颜色
export const getColor = (color, name) => {
  if (!color.originalColorCount) {
    color.originalColorCount = 0;
  }
  const { colorContentSource, colorType } = color;
  let originalColorContent = color[`${colorType}_original_content`];
  if (!(originalColorContent && originalColorContent.length)) {
    originalColorContent = color.colors;
  }
  let customColorObj;
  let targetColor;

  if (colorContentSource === 'custom') {
    const colorContent = color[`${colorType}_${colorContentSource}_content`];
    customColorObj = colorContent.find(d => d.key === name);
  }
  if (customColorObj) {
    if (colorType === 'single') {
      [targetColor] = customColorObj.color;
    } else {
      targetColor = customColorObj.color;
    }
  } else {
    targetColor = originalColorContent[color.originalColorCount % originalColorContent.length];
    color.originalColorCount++;
    color.timer && clearTimeout(color.timer);
    color.timer = setTimeout(() => {
      color.originalColorCount = 0;
    }, 500);
  }
  return targetColor;
};

// 图表-根据数据类型返回指定的颜色值
export const getSerieColor = color => {
  if (Array.isArray(color)) {
    return {
      type: 'linear',
      x: 0,
      y: 0,
      x2: 0,
      y2: 1,
      colorStops: [
        {
          offset: 0,
          color: color[0], // 0% 处的颜色
        },
        {
          offset: 1,
          color: color[1], // 100% 处的颜色
        },
      ],
    };
  } else {
    return color;
  }
};

// 自定义颜色主题专用，获取主题颜色数组
export const getThemeColors = (color, colorGroup) => {
  let colors = [];
  if (typeof color === 'object') {
    colors = color.colors;
  } else {
    if (!colorGroup[color]) {
      const keys = Object.keys(colorGroup);
      colors = colorGroup[keys[0]];
    } else {
      colors = colorGroup[color];
    }
  }
  return colors;
};

/**
 * 根据额外配置设置图表
 * @param {*} option 图表配置
 * @param {*} extraConfig 额外的配置参数
 */
export const setExtraConfig = (option, extraConfig) => {
  // isLast: 在x轴共用组件中，图表是否为最后一个
  const { isLast = true, marklineConfig = {} } = extraConfig;
  if (!isLast) {
    option.xAxis.nameTextStyle = {
      color: 'rgba(0,0,0,0)',
    };
    option.xAxis.axisLabel.show = false;
    if (option.dataZoom.length) {
      option.dataZoom[0].show = false;
    }
  }

  // 添加参考线
  if (marklineConfig.isMarklineShow && marklineConfig.hasMarkLine) {
    const markLineData = [];
    marklineConfig.markLine.forEach((markline, index) => {
      markLineData.push({
        name: `${$demoi18n('word.referenceValue')}${index + 1}`,
        yAxis: markline.value,
        lineStyle: {
          width: 2,
          opacity: 1,
          color: markline.color,
          type: 'dotted',
        },
      });
    });

    option.series[0].markLine = {
      silent: true,
      data: markLineData,
      symbol: ['none', 'none'],
      label: {
        show: false,
      },
      emphasis: {
        label: false,
      },
    };
  }
};

// 根据类型判断值是否符合要求
export const checkStringType = (type, str) => {
  let flag = false;
  const validStr = {
    text:
      'string,varchar,char,text,jsonb,json,interval,inet,box,cidr,circle,uuid,' +
      'point,path,money,macaddr,lseg,line,txid_snapshot,tsvector,tsquery,polygon,',
    number:
      'bigint,tinyint,smallint,int,double,float,decimal,bit,varbit,bool,bytea,' +
      'int8,int4,int2,float8,float4,serial8,serial4,serial2,numeric,integer',
    date: 'date,timestamp,timetz,timestamptz,time,',
  };
  if (!type || !str) {
    return flag;
  }
  if (validStr[type].includes(str)) {
    flag = true;
  }
  return flag;
};

// 根据当前分辨率(innerWidth)转换数值
export const convertValue = (value, minValue = 0, rateType = '') => {
  const { rateW, rateH, rate } = store.state.VisualComponentsModule;
  let _rate = rate;
  if (rateType === 'w') {
    _rate = rateW;
  } else if (rateType === 'h') {
    _rate = rateH;
  } else {}
  const v = value * _rate;
  return v < minValue ? minValue : v;
};

export const convertTableValue = (value, minValue = 0) => {
  let innerWidth;
  if (window.innerWidth < 1280) {
    innerWidth = 1280;
  } else {
    innerWidth = window.innerWidth;
  }
  const v = (value * innerWidth) / 1920;
  return v < minValue ? minValue.toFixed(0) : v.toFixed(0);
};

// 获取转换后适当换行的文本
export const getConvertedString = (str, len) => {
  const sum = str.toString().length;
  const _len = len > 1 ? len : 1;
  const times = Math.ceil(sum / _len);
  let newStr = '';
  let num = 0;
  for (let index = 0; index < times; index++) {
    newStr += `${str.toString().slice(num, num + _len)}`;
    if (index + 1 !== times) {
      newStr += '\n';
    }
    num = num + _len;
  }
  return newStr;
};

// 是否空对象
export const isEmptyObject = obj => Object.keys(obj || {}).length === 0;

// 根据标签动态调整字体大小
export const adjustFontSize = html => {
  const fzBasic = {
    p: 16,
    h1: 2 * 16,
    h2: 1.5 * 16,
    h3: 1.17 * 16,
    h4: 1.15 * 16,
    h5: 0.83 * 16,
    h6: 0.75 * 16,
  };
  let temp = document.createElement('div');
  temp.innerHTML = _xss(html);
  const nodeList = temp.querySelectorAll('*');
  for (let i = 0; i < nodeList.length; i++) {
    let fontSize;
    const el = nodeList[i];
    if (el.className) {
      el.className.replace(/ql-fontSize-(\d+)px/, (s, s1) => {
        if (s1) {
          fontSize = Number(s1);
        }
      });
    }

    if (!fontSize) {
      const tagName = el.tagName.toLowerCase();
      fontSize = fzBasic[tagName];
    }

    if (!fontSize) {
      fontSize = el.style.fontSize.replace('px', '');
      fontSize = Number(fontSize) || 0;
    }

    if (fontSize) {
      el.style.fontSize = `${convertValue(fontSize, 12)}px`;
    }
  }
  const _html = temp.innerHTML;
  temp = null;

  return _html;
};

// 深拷贝变量
export const clone = variable => JSON.parse(JSON.stringify(variable));

// js原生matchAll方法兼容性有问题，使用此函数代替
export const matchAll = (str, regexp, index = 0) => {
  const arr = [];
  let res;
  while (regexp.exec(str)) {
    res = regexp.exec(str);
    arr.push(res[index]);
  }
  return arr;
};

/**
 * 获取echart的x轴或者y轴type类型
 * @param  {String,Number,Array} x 输入变量
 * @return {String}   类型：category,time,value
 * --------------------------------------------
 * '2020-01-01' => 'time'
 * '吉利莲' => 'category'
 * '220' => 'value'
 * [200,'吉利莲'] => 'category'
 * --------------------------------------------
 */
export const getEchartXYAxisType = x => {
  let type;
  if (Array.isArray(x)) {
    const types = x.map(n => getEchartXYAxisType(n));
    const hasCategory = types.some(n => n === 'category');
    const hasTime = types.some(n => n === 'time');
    const hasValue = types.some(n => n === 'value');
    if (hasCategory || (hasTime && hasValue)) {
      type = 'category';
    } else if (hasTime) {
      type = 'time';
    } else {
      type = 'value';
    }
  } else {
    if (isNaN(x)) {
      if (!isNaN(Date.parse(x))) {
        type = 'time';
      } else {
        type = 'category';
      }
    } else {
      type = 'value';
    }
  }
  return type;
};

// 通过config 转换出对应的数据
export const transformStaticDataByConfig = (data, tableData) => {
  const finalData = [];
  if (data) {
    data.forEach(item => {
      const dataItem = {};
      tableData.forEach(tableItem => {
        if (tableItem.mapping) {
          dataItem[tableItem.field] = item[tableItem.mapping];
        }
      });
      finalData.push(dataItem);
    });
  }
  return finalData;
};

export const transformStaticDataByProperty = (data, tableData, propertyTableData)=>{
  const finalData = [];
  if (data) {
    data.forEach(item => {
      const dataItem = {};
      tableData.forEach(tableItem => {
        if (tableItem.mapping) {
          const currentIcon = propertyTableData.find(({ values })=>values.split(';').includes(item[tableItem.mapping]));
          dataItem[tableItem.field] = item[tableItem.mapping];
          currentIcon && (dataItem.icon = currentIcon.icon);
        }
      });
      finalData.push(dataItem);
    });
  }
  return finalData;
};

// dataSourceType是否为静态数据
export const isStaticData = dataSourceType => dataSourceType === 'staticData';

// 获取或设置当前动作类型，用于区分不同操作（保存组件，页面加载）
export const visualAction = (action = '', timeout = 10000) => {
  if (action) {
    window.visualComp.action = action;
    clearTimeout(window.visualComp.actionTimer);
    window.visualComp.actionTimer = setTimeout(() => {
      // 10秒后清除动作标记
      delete window.visualComp.action;
      delete window.visualComp.actionTimer;
    }, timeout);
    return true;
  }

  const _action = window.visualComp.action;
  return _action;
};

/**
 * 将多层对象转换成扁平对象
 * @param  {Object} obj    多层对象， 如 {a: aa: {aaa: 'hello'}}
 * @param  {String} pKey   顶层属性key
 * @param  {Object} resObj 返回扁平对象
 * @return {Object}        扁平对象
 *                         如：{
 *                                'echart.a.aa.aaa': 'hello',
 *                                'echart.a.aa.aaa1.0': 111,
 *                                'echart.a.aa.aaa1.1': 222,
 *                                'echart.b.0.bb': 333,
 *                                'echart.b.1.0.bb1': 444
 *                              }
 */
export const getFlatObj = (obj, pKey = '', resObj = {}) => {
  const _resObj = resObj || {};
  if (isArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      const key = pKey ? `${pKey}.${i}` : i;
      getFlatObj(obj[i], key, _resObj);
    }
  } else if (isObject(obj)) {
    Object.entries(obj).map(([i]) => {
      const key = pKey ? `${pKey}.${i}` : i;
      getFlatObj(obj[i], key, _resObj);
    });
  } else {
    _resObj[pKey] = obj;
  }
  return _resObj;
};

// 使用xss过滤对象中的指定key
export const xssFilter = (obj, keys = []) => {
  // 没有keys时对obj全字段过滤
  let _keys = keys;
  if (!_keys.length) {
    _keys = Object.keys(obj);
  }
  _keys.forEach(key => {
    const n = obj[key];
    if (isObject(n)) {
      Object.entries(n).map(([i]) => {
        n[i] = _xss(n[i], {
          whiteList,
        });
      });
    } else if (isArray(n)) {
      for (let i = 0; i < n.length; i++) {
        n[i] = _xss(n[i], {
          whiteList,
        });
      }
    } else {
      if (typeof obj[key] === 'string') {
        obj[key] = _xss(obj[key], {
          whiteList,
        });
      }
    }
  });
  return obj;
};

// 递归使用xss过滤对象中所有的子属性
export const xssFilterAll = (obj, key = '', pObj = {}) => {
  if (isArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      xssFilterAll(obj[i], i, obj);
    }
  } else if (isObject(obj)) {
    Object.entries(obj).map(([i]) => {
      xssFilterAll(obj[i], i, obj);
    });
  } else {
    if (typeof obj === 'string') {
      pObj[key] = _xss(obj, {
        whiteList,
      });
    }
  }
};

export const xssValueFilter = (value, forType = '') => {
  const _whiteList = { ...whiteList };
  let _value = value;
  if (forType === 'editor') {
    // 高级tooltip，浮动文本
    _value = _value.replace(/href=["'][^'"]+["']/g, ''); // 环境上还是能显示url，手工去除一下
    delete _whiteList.a; // 去除a标签
  }
  _value = _xss(_value, {
    whiteList: _whiteList,
  });
  return _value;
};

export const colorRGB2Hex = color => {
  if (!color || (!color.includes('(') && color.includes('#'))) {
    return color;
  }
  const rgb = color.split(',');
  const r = parseInt(rgb[0].split('(')[1]);
  const g = parseInt(rgb[1]);
  const b = parseInt(rgb[2].split(')')[0]);

  const hex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
  return hex;
};

export const HexToRgb = (str, returnArr = false) => {
  const r = /^\#?[0-9a-fA-F]{6}$/; // test方法检查在字符串中是否存在一个模式，如果存在则返回true，否则返回false
  if (!r.test(str)) {
    return showErr($demoi18n('util.enterAnIncorrectHex'));
  } // replace替换查找的到的字符串
  const _str = str.replace('#', ''); // match得到查询数组
  const hxs = _str.match(/../g);
  for (let i = 0; i < 3; i++) {
    hxs[i] = parseInt(hxs[i], 16);
  }
  if (returnArr) {
    return hxs;
  }
  return `rgb(${arr.join(', ')})`;
};

export const RgbToHex = (a, b, c) => {
  const r = /^\d{1,3}$/;
  if (!r.test(a) || !r.test(b) || !r.test(c)) {
    return showErr($demoi18n('util.enterAnIncorrectRgbColorValue'));
  }
  const hexs = [Number(a).toString(16), Number(b).toString(16), Number(c).toString(16)];
  for (let i = 0; i < 3; i++) {
    if (hexs[i].length === 1) {
      hexs[i] = `0${hexs[i]}`;
    }
  }
  return `#${hexs.join('')}`;
};

export const colorToRgba = (color, opacity) => {
  let _color = color.trim();
  let rgba = '';
  if (_color.indexOf('rgb') === 0) {
    const arr = [];
    _color.replace(/[\d\.]+/g, s => {
      arr.push(s);
    });
    arr[3] = opacity || (arr[3] ? arr[3] : 1);
    rgba = `rgba(${arr.join(', ')})`;
  } else if (/^\#?[0-9a-fA-F]{3,8}$/.test(_color)) {
    switch (_color.length) {
      case 4:
        _color = _color.replace(/[0-9a-fA-F]/g, s => `${s}${s}`);
        break;
      case 9:
        _color = _color.slice(0, 7);
        break;
      default: break;
    }
    const rgb = HexToRgb(_color, true);
    rgba = `rgba(${rgb.join(', ')}, ${opacity || 1})`;
  } else {}
  return rgba;
};

export const getLightColor = (color, level) => {
  const r = /^\#?[0-9a-fA-F]{6}$/;
  if (!r.test(color)) {
    return showErr($demoi18n('util.enterAnIncorrectHexColorValue'));
  }
  const rgbc = HexToRgb(color, true);
  for (let i = 0; i < 3; i++) {
    rgbc[i] = Math.floor((255 - rgbc[i]) * level + rgbc[i]);
  }
  return RgbToHex(rgbc[0], rgbc[1], rgbc[2]);
};

// 根据window窗口宽度自适应获取css样式
export const getCssStyle = (style = {}, resize = true) => {
  let visualModule = store.state.VisualComponentsModule;
  if (process.env.NODE_ENV === 'production') {
    visualModule = window._xvueStore.state.VisualComponentsModule;
  }
  const { rateW, rateH, rate } = visualModule;
  const _style = JSON.parse(JSON.stringify(style));
  const resizeProps = {
    fontSize: rate, width: rateW, height: rateH, lineHeight: rate, marginTop: rateH,
    marginBottom: rateH, marginLeft: rateW, marginRight: rateW, paddingTop: rateH,
    paddingBottom: rateH, paddingLeft: rateW, paddingRight: rateW, borderRadius: rate,
    borderWidth: rate, top: rateH, bottom: rateH, left: rateW, right: rateW, textIndent: rate, lineGap: rateH,
  };

  Object.entries(_style).map(([i]) => {
    let orgVal = _style[i];

    // 对象类型 width: {switch:false, value: 200}
    if (isObject(_style[i])) {
      // 背景图片 backgroundColor
      if (_style[i].icon_id && i === 'backgroundImage') {
        _style[i] = `url(${getServerImgUrlByIcon(_style[i], false)})`;
        return true;
      }

      orgVal = _style[i].value;
      if (_style[i].switch) {
        _style[i] = orgVal;
      } else {
        delete _style[i];
        return true;
      }
    }

    if (/^-?[0-9]+\.?[0-9]*$/.test(orgVal)) {
      const oldVal = Number(orgVal);
      const _rate = resizeProps[i];
      if (_rate) {
        let minVal = 0;
        if (oldVal < 0) {
          minVal = oldVal;
        }
        if (i === 'fontSize' || i === 'lineHeight') {
          minVal = 12;
        }
        let val = orgVal * _rate;
        if (val < minVal) {
          val = minVal;
        }
        _style[i] = resize ? `${val}px` : `${orgVal}px`;
      }
    }
    if (i === 'backgroundColor' && orgVal && orgVal !== 'transparent') {
      _style[i] = `${orgVal}!important`;
    }
    return true;
  });
  if (_style.textDecoration === 'underline') {
    _style.cursor = 'pointer';
  }
  return _style;
};

// 处理null值
export const resetNull = (obj = {}, nullSet = null) => {
  Object.entries(obj).map(([val]) => {
    obj[val] = obj[val] === null ? nullSet : obj[val];
  });
  return obj;
};

export const rgba2hex = orig => {
  let a;
  const rgb = orig.replace(/\s/g, '').match(/^rgba?\((\d+),(\d+),(\d+),?([^,\s)]+)?/i);
  const alpha = ((rgb && rgb[4]) || '').trim();
  let hex = rgb ?
    (rgb[1] | (1 << 8)).toString(16).slice(1) +
        (rgb[2] | (1 << 8)).toString(16).slice(1) +
        (rgb[3] | (1 << 8)).toString(16).slice(1) :
    orig;

  if (alpha !== '') {
    a = alpha;
  } else {
    a = '01';
  }
  a = ((a * 255) | (1 << 8)).toString(16).slice(1);
  hex = hex + a;

  return `#${hex}`;
};

export const setBoxBackground = (property, target) => {
  const { backgroundColor } = property;
  if (backgroundColor) {
    target.backgroundColor = `${backgroundColor}!important`;
  }
};

/*
 * 数值运算有可能产生很长小数位
 * 如 1.11 - 1 = 0.1100000000000001
 * 如 2.4 / 0.8 = 2.9999999999999996
 * 使用该函数修正
 */
export const fixCalcNum = (num = 0) => {
  if (!isNum(num)) {
    return num;
  }
  const rate = 100000000;
  const fixNum = Math.round(num * rate) / rate;
  return fixNum;
};

// 环形图、饼图负数提示
export const checkNegative = result => {
  let flag = false;
  const tipName = [];
  for (let i = 0; i < result.length; i++) {
    if (result[i].y1 < 0 || result[i].y < 0) {
      flag = true;
      tipName.push(result[i].s);
    }
  }
  if (flag) {
    Message.error(`${tipName.join('、')} ${$demoi18n('message.ringIndicatorErrorTip')}`);
  }
};

// 判断变量是否promise对象
export const isPromise = obj => (
    Boolean(obj) && // 有实际含义的变量才执行方法，变量null，undefined和''空串都为false
    (typeof obj === 'object' || typeof obj === 'function') && // 初始promise 或 promise.then返回的
    typeof obj.then === 'function'
);

/*
 * 弹窗消息
 * element-ui的this.$message方法如果多个消息同时执行，会出现消息相互重叠现象，使用该函数可规避重叠
 */
export const $message = option => {
  if (!window.visualComp.messages) {
    window.visualComp.messages = [];
  }
  const msgs = window.visualComp.messages;
  if (option) {
    msgs.push(option);
  }

  const showMsg = async() => {
    if (msgs[0]) {
      if (!msgs[0].customClass) {
        msgs[0].customClass = `visualComponents_theme_${window._adoPremise.uc.theme}`;
      }
      const msg = { offset: 20, ...msgs[0] };

      // 设置垂直偏移距离
      const messageVm = await Message(msg);
      if (option.verticalOffset) { // 数值型如： verticalOffset: 40
        messageVm.verticalOffset = option.verticalOffset;
      }

      msgs.shift();
      await showMsg();
      if (msgs.length === 0) {
        delete window.visualComp.messagesLock;
      }
    }
  };

  // 加锁，防止同一时间多次运行showMsg方法
  const isLock = window.visualComp.messagesLock;
  if (!isLock) {
    window.visualComp.messagesLock = true;
    showMsg();
  }
};

// 获取组件筛选参数对象，跟组件的this.pageStatus是相等的，都是从session中获取
export const getVmPageStatus = vm => {
  const pageId = getPageId(vm);
  const { tenantId } = window._adoPremise;
  const sKey = `${pageId}_${tenantId}`;
  const pageStatus = storage.session.get(sKey) || {};
  return pageStatus;
};

// 校验所有子form
export const validChildForms = async formVms => {
  const errors = [];
  if (formVms.length > 0) {
    await Promise.all(
      formVms.map(formVm => new Promise(resolve => {
        formVm.validate((valid, obj) => {
          if (!valid) {
            errors.push(obj);
            resolve(false);
          } else {
            resolve(true);
          }
        });
      })),
    );
  }
  return errors;
};

/**
 * 使用html节点入参校验子form
 * @param {dom} rooterEl 根html节点
 * @returns {Array}
 * 注意：子form需要添加 v-visaul-form指令
 */
export const validChildFormsByEl = async rooterEl => {
  const errors = [];
  let formEls = [];
  if (rooterEl.classList.contains('el-form')) {
    formEls.push(rooterEl);
  }
  formEls = [
    ...formEls,
    ...rooterEl.querySelectorAll('.el-form'),
  ];
  formEls = formEls.filter(n => n.validate);
  if (!formEls.length) {
    return errors;
  }
  await Promise.all(
    formEls.map(formEl => new Promise(resolve => {
      formEl.validate((valid, obj) => {
        if (!valid) {
          errors.push(obj);
          resolve(false);
        } else {
          resolve(true);
        }
      });
    })),
  );
  return errors;
};

export const $confirm = (message, title, options) => {
  const args = options;
  args.customClass = `visualComponents_theme_${window._adoPremise.uc.theme}`;
  return ElMessageBox.confirm(message, title, args);
};

export const $warnConfirm = (message, title, options = {}) => {
  const args = {
    confirmButtonText: $demoi18n('btn.confirm'),
    cancelButtonText: $demoi18n('btn.cancel'),
    type: 'warning',
    customClass: `visualComponents_theme_message_${window._adoPremise.uc.theme}`,
    ...options,
  };
  return ElMessageBox.confirm(message, title, args);
};

// 延时
export const sleep = timeMs => new Promise(resolve => {
  setTimeout(resolve, timeMs);
});

// 获取原生tooltip小圆点
export const getTooltipMarker = color => {
  let bgColor = color;
  if (isObject(color)) {
    if (color.colorStops) {
      bgColor = color.colorStops[0].color;
    }
  }
  const marker = '<span style="display:inline-block;margin-right:5px;' +
                `border-radius:10px;width:10px;height:10px;background-color:${bgColor};"></span>`;
  return marker;
};

// 设置url传参
export const setUrlParam = (key, val, url) => {
  let _url = url || window.location.href;
  const splitor = _url.includes('?') ? '&' : '?';
  const _val = encodeURIComponent(val);
  if (_url.indexOf(`${key}=`) === -1) {
    _url = `${_url}${splitor}${key}=${_val}`;
  } else {
    const regex = new RegExp(`${key}=([^&]+)`);
    _url = _url.replace(regex, `${key}=${_val}`);
  }
  return _url;
};

// 对象对比相减，适用于对象，二维数组
export const objDiff = (obj, base) => {
  function diff(_obj, _base) {
    return _.transform(_obj, (result, value, key) => {
      if (!_.isEqual(value, _base[key])) {
        result[key] = isObject(value) && isObject(_base[key]) ? diff(value, _base[key]) : value;
      }
    });
  }
  return diff(obj, base);
};

// 前端导出 防DDE注入
export const ddeFilter = value => {
  if (isEmpty(value)) {
    return '';
  } else {
    let str = value.toString();
    if (str.match('^[=\\+\\-@].*')) {
      str = `\t${str.replaceAll('"', '""')}`;
    } else {
      str = str.replace('"', '""');
    }
    return str;
  }
};

// 获取ie版本，同时也可以用来判断是否ie浏览器
export const isIE = (exludeEdge = false) => {
  const { userAgent } = navigator;
  const _isIE = userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1; // 判断是否IE<11浏览器
  const isEdge = userAgent.indexOf('Edge') > -1 && !_isIE; // 判断是否IE的Edge浏览器
  const isIE11 = userAgent.indexOf('Trident') > -1 && userAgent.indexOf('rv:11.0') > -1;
  if (_isIE) {
    return true;
  }
  if (isIE11) {
    return true;
  }
  if (isEdge && !exludeEdge) {
    return true;
  }
  return false;
};

// 单词首字母转大写 hello => Hello
export const firstToUpper = str => str
  .trim()
  .toLowerCase()
  .replace(str[0], str[0].toUpperCase());

// 跳转地址修正
export const fixUrl = url => {
  if (!url) {
    return url;
  }
  const { protocol, host, port } = window.location;
  let _url = url;
  if (!_url.startsWith('/') && !_url.startsWith('http')) {
    _url = `/${_url}`;
  }
  if (!_url.startsWith('http')) {
    _url = window._adoPremise.baseUrl + _url;
  }
  _url = _url.replace('/xx/xvueui-realportal/', '/xvueui/');
  return _url;
};

/**
 * 网址跳转，兼容ie
 * @param  {String}  jumpUrl       目标网址
 * @param  {Boolean} isOpenNewPage 是否新页签或窗口打开
 * @param  {Object}  vm            vue实例
 */
export const jump = (jumpUrl, isOpenNewPage = true, vm = null) => {
  const _jumpUrl = fixUrl(jumpUrl);
  const isIe = isIE(true);
  if (isOpenNewPage) {
    let params = 'noopener=yes,noreferrer=yes';
    if (isIe) {
      // ie浏览器打开时，新在新窗口（非新页签打开），有可能不带url输入栏
      params += ',location=yes';
    }
    window.open(_jumpUrl, '_blank', params);
  } else {
    window.location.href = _jumpUrl;

    // ie下hash跳转无效修复
    if (isIe) {
      const currentPath = window.location.hash.slice(1);
      vm.$router.push(currentPath);
    }
  }
};

export const getObjFromJson = str => {
  try {
    const obj = JSON.parse(str);
    if (typeof obj === 'object') {
      return obj;
    }
  } catch (e) {}
  return false; // 返回false表示不是json
};

// ----------- 加载dev/funs.js下定义的函数
export const { safeEval } = funs;

// 将参数保存到xvue接口, 传参根据xvue后端需要重构
export const refactorApis = apis => {
  const res = _.pick(apis, ['apiCode', 'isCommonDatasource', 'datasourceCode', 'curComp']);
  const { params, pageIndex } = apis;
  if (params) {
    res.params = params.map(ele => _.pick(ele, ['code', 'value', 'wid', 'isCommon']));
  }
  if (pageIndex) {
    res.pageIndex = 1;
  }
  return res;
};

/**
 * dom节点可见不可见切换绑定事件
 * @param  {dom} el          dom节点
 * @param  {function} visibleFn   可见状态下触发函数
 * @param  {function} unvisibleFn 不可见状态触发函数
 */
export const bindVisibleChange = (el, visibleFn, unvisibleFn) => {
  const observer = new IntersectionObserver(
    entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          visibleFn?.();
        } else {
          unvisibleFn?.();
        }
      });
    },
    {
      root: null, // 观察器的根节点，默认为浏览器视窗
      threshold: 0.5, // 目标元素与根节点相交的阈值
    },
  );

  observer.observe(el);
};

/**
 * 将对象的键和值互换，并返回一个新的对象。
 * @param {Object} obj - 要进行键值互换的对象。
 * @returns {Object} - 互换键值后的新对象。
 */
export const swapKeyValue = obj => Object.entries(obj).reduce((acc, [key, value]) => {
  if (value !== '' && value !== null && value !== undefined) {
    acc[value] = key;
  }
  return acc;
}, {});

/**
 * 将变量转为字符串
 * @param  {mixed} val
 * @return {string}
 * @example
 *   0 => '0'
 *   undefined => ''
 *   null => ''
 *   true => 'true'
 *   {a:1} => '{"a":1}'
 *   [1,2] => '[1,2]'
 */
export const toString = val => {
  if (isUndefinedNull(val)) {
    return '';
  } else if (isObject(val) || isArray(val)) {
    return JSON.stringify(val);
  } else {
    return String(val);
  }
};

/**
 * 转驼峰
 * @param  {String} input    输入字符串
 * @return {String}
 * @example
 *   'hello_world' => 'helloWorld'
 *   'hello-world' => 'helloWorld'
 *   'hello-world_name' => 'helloWorldName'
 */
export const camelCase = input => {
  let x = input.replace(/[-_]([A-Za-z])/g, (__, c) => c.toUpperCase());
  x = x.charAt(0).toLowerCase() + x.slice(1);
  return x;
};

/**
 * 判断两个对象是否相等
 * @param  {Object} obj1
 * @param  {Object} obj2
 * @return {Boolean}
 */
export const isObjEqual = (obj1, obj2) => {
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  if (keys1.length !== keys2.length) {
    return false;
  }
  for (const key of keys1) {
    if (obj1[key] !== obj2[key]) {
      return false;
    }
  }
  return true;
};

/**
 * 将 {color:{light:'xx',dark:'yy'}} 转换成 {light:{color:'xx'}, dark:{color:'yy'}}结构
 * @param  {object} source 源风格对象
 * @return {object}        转换后对象
 */
export const tranThemeObj = source => {
  const result = {};
  Object.entries(source).map(([key]) => {
    if (typeof source[key] === 'object') {
      const nested = tranThemeObj(source[key]);
      Object.entries(nested).map(([nestedKey]) => {
        result[nestedKey] = result[nestedKey] || {};
        result[nestedKey][key] = nested[nestedKey];
      });
    } else {
      if (!result[key]) {
        result[key] = {};
      }
      result[key] = source[key];
    }
  });
  return result;
};

// 处理

/**
 * 合并echart组件色板和公共色板
 * @param  {object} colorBoard 组件自身色板
 * @return {object}            合并后色板
 */
let _colorBoardAll;
export const mergeColorBoard = colorBoard => {
  if (!_colorBoardAll) {
    _colorBoardAll = tranThemeObj(colorBoardAll);
  }
  const mergeBoard = {};
  mergeObj(mergeBoard, clone(_colorBoardAll), colorBoard);
  return mergeBoard;
};

// 添加数组子项随机key，用于v-for中使用key绑定（用数组索引做key，有可能删除/排序时会混乱）
export const addArrRandKey = obj => {
  if (Array.isArray(obj)) {
    obj.forEach(item => {
      if (Array.isArray(item)) {
        addArrRandKey(item); // 递归处理嵌套的数组
      }
      if (isObject(item)) {
        addArrRandKey(item); // 递归处理嵌套的对象
      }
      item._randKey = Math.random().toString(36).substring(2); // 为数组的每一项添加 _randKey 属性
    });
    return;
  }
  if (isObject(obj)) {
    Object.entries(obj).map(([key]) => {
      if (Array.isArray(obj[key])) {
        addArrRandKey(obj[key]); // 递归处理嵌套的数组
      }
      if (isObject(obj[key])) {
        addArrRandKey(obj[key]); // 递归处理嵌套的对象
      }
    });
  }
};

// 删除对象或数组下面子项的 _randKey
export const delArrRandKey = obj => {
  if (Array.isArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      const item = obj[i];
      if (Array.isArray(item) || isObject(item)) {
        delArrRandKey(item);
      }
    }
    return;
  }
  if (isObject(obj)) {
    Object.entries(obj).map(([key]) => {
      const value = obj[key];
      if (Array.isArray(value) || isObject(value)) {
        delArrRandKey(value);
      }
    });
    delete obj._randKey;
  }
};

export const getVappDir = () => {
  let baseUrl = '';
  if (window.isLocalEnv) {
    baseUrl = '/public';
  } else {
    // const vapp = window._xvueStore.state.XVue_AppManager.myApps.find(n => n.packageName === `_${process.env.xvueNameSpace}`);
    // if (vapp) {
    //   baseUrl = vapp.repositoryUrl.replace(/\/lib\/.*$/, '/lib');
    // }
    const repositoryUrl = window.xVueWeb.app.config.globalProperties.$esmLoadAppUrls.find(n => n.includes(`/${process.env.xvueNameSpace}/`));
    baseUrl = repositoryUrl.replace(/\/lib\/.*$/, '/lib');
  }
  return baseUrl;
};

// 动态加载css链接
export const loadCssLink = url => {
  const links = document.getElementsByTagName('link');
  for (let i = 0; i < links.length; i++) {
    if (links[i].href === url) {
      return;
    }
  }

  const head = document.getElementsByTagName('HEAD')[0];
  const link = document.createElement('link');
  link.rel = 'stylesheet';
  link.type = 'text/css';
  link.href = url;
  head.appendChild(link);
};

export const loadVappCssLink = mode => {
  const baseUrl = getVappDir();
  const cssLink = `${baseUrl}/${mode}/${process.env.xvueNameSpace}.${mode}.css`;
  loadCssLink(cssLink);
};
export const getXvueTheme = () => {
  if (window?.xVueWeb?.uc?.xvueTheme) {
    const reg = new RegExp('xvue-(.*)?-theme', 'ig');
    return reg.test(window.xVueWeb.uc.xvueTheme) ? (RegExp.$1 || 'light') : 'light';
  }
  return window._adoPremise.uc.theme || 'light';
};

export const xvueTheme = getXvueTheme();

/**
 * 拼接状态切换类组件的图片地址
 * @param  {Object} data
 * @return {Object}
 */
export const getSrc = data => {
  const currData = clone(data);
  if (currData.icon?.isDefaut) {
    const src = clone(currData.icon.imgPath);
    currData.icon.src = new URL(`${getBaseUrl}${src}`, import.meta.url).href;
  }
  return currData;
};